﻿/* 
 *  <copyright file="GameplaySpawner.cs" company="Wesley Jowitt">
 *  
 *  SobrietyEngine: Copyright (c) 2011 Wesley Jowitt
 *  
 *  This software is distributed under a software license.
 *   * As of the creation time of this file, the license in use is the Microsoft Permissive License (Ms-PL) 
 *    * The license can be viewed at:
 *     * License.Txt in the root folder of this project
 *      * http://sobrietyengine.codeplex.com/license
 *       * http://www.microsoft.com/opensource/licenses.mspx
 *       
 *  </copyright>
 *   */

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SEGameExample.Components;
using SEGameExample.Components.Controllers;
using SobrietyEngine.Actor;
using SobrietyEngine.Assets.Textures;
using SobrietyEngine.Common;
using SobrietyEngine.Components;
using SobrietyEngine.Components.Camera;
using SobrietyEngine.Components.Common;
using SobrietyEngine.Components.Graphic;
using SobrietyEngine.Components.Physics;
using SobrietyEngine.Components.Physics.Movers;
using SobrietyEngine.Components.Physics.OnCollision;
using SobrietyEngine.Components.Common;
using SobrietyEngine.Components.Trigger;
using SobrietyEngine.Geometry;
using SobrietyEngine.Guids;
using SobrietyEngine.PathFinding;
using SobrietyEngine.Scene;
using SobrietyEngine.Physics;

//THis is a kludge because all of this should be handled
//through actor actorDefinition files. This will be converted once the majority of the components have been
//finalized.
namespace SEGameExample.Kludge
{
    static class GameplaySpawner
    {

        public static IActor PlatformerPlayer2D(IScene scene, Vector2 pos, CGuid userGuid, GraphicsDevice graphicsDevice, FarseerPhysics.Dynamics.Category groundCat, TextureManagerGameService textureManager)
        {
            IActor actor = scene.CreateEmptyActor();
            actor.AttachComponent(new PositionCmp(pos));
            actor.AttachComponent(new OrientationCmp());


            //lets test using the parameters parsing and scriptable values
            Parameters parms = new Parameters();
            //lets add them all as strings to force the parsing to convert object types            
            //parms["WalkAccel"] = "1.0";
            //parms["JumpAccel"] = "1.0";
            //create controller (should do this via factory)
            SEGameExample.Components.Controllers.User2DPlatform controller = 
                new SEGameExample.Components.Controllers.User2DPlatform(userGuid);
            //manually call set
            //controller.Set(parms);
            //attach
            actor.AttachComponent(controller);

            actor.AttachComponent(new ScaleCmp(new Vector3(2,2,2)));
            actor.AttachComponent(new OnGroundCmp(groundCat));
            actor.AttachComponent(new PlayerLegAnimatorCmp());

            //test loading some animations
            textureManager.LoadAnimation("AnimCfgs/PlayerLegs");
            textureManager.LoadAnimationTextures("PlayerLegs");
            actor.AttachComponent(new AnimatedQuadCmp("PlayerLegs", "Running", true));
           
            FarseerPhysicsCmp pb = new CircleBodyCmp(scene.Name, 1.0f);
            actor.AttachComponent(pb);
           
            actor.Initialize();

            //test creating a revolute joint and attaching the a rectangular hitbox to it            
            Vector2 hbAnchor = new Vector2(0, -0.25f);
            Vector2 myAnchor = new Vector2(0, 0);
            IActor hitBox = CreateHitBox(scene, new Vector2(1, 2), new Vector3(pos, 0), actor, hbAnchor, myAnchor);

            //collision catagory stuff
            pb.Body.CollisionCategories = CollisionCatagories.MoversAre;
            pb.Body.CollidesWith = CollisionCatagories.MoversHit;

            return actor;
        }

        public static IActor CreateHitBox(IScene scene, Vector2 size, Vector3 pos, IActor attachedTo, Vector2 thisAnchor, Vector2 otherAnchor)
        {
            IActor act = scene.CreateEmptyActor();
            act.AttachComponent(new PositionCmp(pos));

            FarseerPhysicsCmp phys = new RectangleBodyCmp(scene.Name, size);
            act.AttachComponent(phys);

            RevoluteJointCmp rjc = new RevoluteJointCmp(scene.Name, thisAnchor, attachedTo, otherAnchor);
            act.AttachComponent(rjc);
            //act.AttachComponent(new AngleJointCmp(Scene.Name, attachedTo, 45.0f));

            act.Initialize();

            //we have a body now, lets change some params
            phys.Body.AngularDamping = float.MaxValue;
            phys.Body.IgnoreGravity = true;
            phys.Body.Mass = 0.00001f;

            //adjust some joint things
            rjc.Joint.CollideConnected = false;

            //collision catagory
            phys.Body.CollidesWith = CollisionCatagories.SolidHitBoxesAre;
            phys.Body.CollidesWith = CollisionCatagories.SolidHitBoxesHit;            

            return act;
        }

        /// <summary>
        /// Creates a circle actor that moves like a goomba in super mario.
        /// </summary>
        /// <param name="Scene">The Scene.</param>
        /// <param name="pos">The pos.</param>
        /// <param name="size">The size.</param>
        /// <returns></returns>
        public static IActor CreateGoomba(IScene scene, Vector2 pos, float size, Vector2 initialDirection)
        {
            IActor actor = scene.CreateEmptyActor();

            actor.AttachComponent(new IdCmp("Goomba"));
            actor.AttachComponent(new PositionCmp(pos));
            actor.AttachComponent(new LinearImpulseMovementCmp(true, initialDirection));
            actor.AttachComponent(new InvertMoverDirOnColCmp(0.1f));

            IFarseerPhysicsBody fb = new CircleBodyCmp(scene.Name, size);
            actor.AttachComponent((IComponent)fb);

            actor.Initialize();

            //tweak physics body after init (thats when its created)
            fb.Body.CollisionCategories = CollisionCatagories.MoversAre;
            fb.Body.CollidesWith = CollisionCatagories.MoversHit;

            return actor;
        }

        public static IActor createRectanglePressureTrigger(IScene scene, Vector2 pos, Vector2 size, String triggerName)
        {
            IActor actor = scene.CreateEmptyActor();
            actor.AttachComponent(new IdCmp(triggerName));
            actor.AttachComponent(new PositionCmp(pos));
            actor.AttachComponent(new OrientationCmp());

            //actor.AttachComponent(new TexturedQuadCmp("Textures/GreySquare", Scene.GraphicDeviceService.GraphicsDevice));
            //actor.AttachComponent(new GraphicScaleCmp(new Vector3(size.X, size.Y, 1)));

            FarseerPhysicsCmp phys = new RectangleBodyCmp(scene.Name, size);
            actor.AttachComponent(phys);

            ITrigger trigger = new TriggerCmp(triggerName, true, false, 100, true);
            actor.AttachComponent((IComponent)trigger);                        

            actor.AttachComponent(new TriggerOnCollisionCmp());
            
            actor.Initialize();

            phys.Body.IsSensor = true;
            phys.Body.IgnoreGravity = true;

            phys.Body.CollisionCategories = CollisionCatagories.MoverTriggersAre;
            phys.Body.CollidesWith = CollisionCatagories.MoverTriggersHit;

            return actor;
        }

        public static List<IActor> createCirclePath(IScene scene, Vector2[] nodePos)
        {
            IActor[] actVec = new IActor[nodePos.Length];

            //Test bed for pathfinding data structures
            Graph path = new Graph();
            INode first = null, last = null;
            //create the nodes
            for (int i = 0; i < nodePos.Length; ++i)
            {
                IActor levelNodeActor = scene.CreateEmptyActor();
                actVec[i] = levelNodeActor;
                levelNodeActor.AttachComponent(new PositionCmp(nodePos[i]));

                ActorPositionNode apn = new ActorPositionNode(levelNodeActor);

                levelNodeActor.AttachComponent(new SobrietyEngine.Components.Paths.PathNodeCmp(apn));

                path.AddNode(apn);
                if (i == 0)
                {
                    first = apn;
                    last = apn;
                }
                else
                {
                    path.CreateUniEdge(last, apn);
                    last = apn;
                }

            }
            path.CreateUniEdge(last, first);

            foreach (IActor a in actVec)
            {
                a.Initialize();
            }

            //return the first actor in this circle path
            return new List<IActor>(actVec);
        }

        public static IActor createMovingPlatform(IScene scene, Vector2 size, Vector2 startPos, CGuid startPathNode, Boolean kinematic)
        {
            IActor actor = scene.CreateEmptyActor();

            IMovement mover = new LinearImpulseMovementCmp();
            mover.ClampVelocity = true;
            actor.AttachComponent((IComponent)mover);

           // actor.AttachComponent(new NextAtTargetCmp(startPathNode));
            actor.AttachComponent(new SobrietyEngine.Components.Paths.NextNodeFollower(startPathNode.Resolve<IActor>()));
            actor.AttachComponent(new OrientationCmp());
            actor.AttachComponent(new PositionCmp(startPos));
            actor.AttachComponent(new TexturedQuadCmp("Textures/GreySquare"));
            actor.AttachComponent(new ScaleCmp(new Vector3(size.X, size.Y, 1)));

            FarseerPhysicsCmp fpc = new RectangleBodyCmp(scene.Name, size);
            actor.AttachComponent(fpc);

            actor.Initialize();

            fpc.Body.FixedRotation = true;                     
            fpc.Body.IgnoreGravity = true;
            fpc.Body.Mass = 100.0f;   
            //every other is kinematic, for testing
            if(kinematic)            
                fpc.Body.BodyType = FarseerPhysics.Dynamics.BodyType.Kinematic;
            else
                fpc.Body.LinearDamping = 10.0f; //keeps our dynamic platorms from being moved by actors            

            fpc.Body.CollisionCategories = CollisionCatagories.GroundIs;
            fpc.Body.CollidesWith = CollisionCatagories.GroundHits;

            return actor;
        }

        public static IActor createWallSegment(IScene scene, LineSegment seg)
        {
            IActor actor = scene.CreateEmptyActor();
            actor.AttachComponent(new PositionCmp(new Vector3(0, 0, 0)));

            FarseerPhysicsCmp pb = new EdgeBodyCmp(scene.Name, seg.points[0], seg.points[1]);
            pb.Enabled = true;
            pb.IsStatic = true;
            actor.AttachComponent(pb);

            actor.Initialize();

            pb.Body.CollisionCategories = CollisionCatagories.GroundIs;
            pb.Body.CollidesWith = CollisionCatagories.GroundHits;

            return actor;
        }

        public static IActor Mesh(IScene scene, string meshName, Game game, Vector2 pos)
        {
            IActor actor = scene.CreateEmptyActor();
            Model m = ((SobrietyEngine.Games.SobrietyGame)game).AssetGameService.Load<Model>(meshName);
            actor.AttachComponent(new SobrietyEngine.Components.Graphic.ModelCmp(m));
            actor.AttachComponent(new PositionCmp(pos));
            actor.Initialize();
            return actor;
        }

        public static void Level(IScene scene, String meshName, Game game)
        {
            IActor actor = scene.CreateEmptyActor();
            //Model m = ScreenManager.Game.Content.Load<Model>("Models/p1_wedge");
            Model m = ((SobrietyEngine.Games.SobrietyGame)game).AssetGameService.Load<Model>(meshName);
            actor.AttachComponent(new SobrietyEngine.Components.Graphic.ModelCmp(m));
            actor.AttachComponent(new PositionCmp(new Vector3(0, 0, 0)));
            actor.Initialize();

            //lets try mesh slicing for physics world
            Plane p = new Plane(new Vector4(0, 0, 1, 0));
            List<LineSegment> sl = Slicer.Slice(m, p);

            sl = Slicer.RemoveShortEdges(sl, 0.25f);

            //for(int a=0; a< 3; ++a)
              //  sl = Slicer.MergeSegments(sl, 0.80f, 0.1f);
                //sl = Slicer.MergeSegments(sl, 0.50f, 0.005f, 0.005f);
            int num = sl.Count;
            int diff = 1;
            int loops = 0;
            //try to reduce amount of static geometry. i think there are a lot of coincident lines.
            while (diff != 0 && num > 1000 && loops < 100)
            {
                sl = Slicer.MergeSegments(sl, 0.75f, 0.1f);
                diff = num - sl.Count;
                num = sl.Count;
                ++loops;
            }

            foreach (LineSegment seg in sl)
            {
                IActor segAct = createWallSegment(scene, seg);
            }
        }

        public static IActor OrthoFollowActorCam3D(IScene scene, Viewport fullscreen, GraphicsDevice graphicsDevice, CGuid followActor)
        {
            //create 3D Scene camera
            //create a camera actor
            IActor actor = scene.CreateEmptyActor();

            Matrix viewScale = Matrix.Identity;
            Viewport view = new Viewport();
            view = fullscreen;
            view.Height = fullscreen.Height;
            view.Width = fullscreen.Width;
            viewScale = Matrix.CreateScale(1.0f, 1.0f, 1.0f);

            //lets have the camera start with its center on playerPos
            actor.AttachComponent(new PositionCmp(new Vector3(0, 0, 10)));
            actor.AttachComponent(new Camera3DCmp(graphicsDevice, view, viewScale));
            actor.AttachComponent(new OrientationCmp());
            //camActor.AttachComponent(new TheVoid.Controllers.Cam3DOrtho(PlayerOne.Guid));
            //lets have it follow our textured quad, above            
            actor.AttachComponent(new SEGameExample.Components.Behavior.OrthoCamFollowActor(followActor, 3, new Vector3(0, 0.5f, 0)));

            actor.Initialize();
            return actor;
        }



    }
}
